/**
 * Notes: 字符相关操作函数
 * Ver : CCMiniCloud Framework 2.5.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
 * Date: 2020-09-05 04:00:00 
 */

/**
 * 数据处理助手
 * 提供数据转换、过滤等功能
 */

/**
 * 数据深度拷贝
 * @param {*} obj 待拷贝数据
 */
function deepClone(obj) {
	if (obj === null || typeof obj !== 'object') {
		return obj;
	}
	
	const result = Array.isArray(obj) ? [] : {};
	
	for (let key in obj) {
		if (Object.prototype.hasOwnProperty.call(obj, key)) {
			result[key] = deepClone(obj[key]);
		}
	}
	
	return result;
}

/**
 * 提取对象中指定的字段
 * @param {Object} obj 对象
 * @param {Array} keys 字段名数组
 * @returns {Object} 提取后的对象
 */
function pick(obj, keys) {
	if (!obj) return {};
	
	const result = {};
	keys.forEach(key => {
		if (obj.hasOwnProperty(key)) {
			result[key] = obj[key];
		}
	});
	
	return result;
}

/**
 * 从对象中排除指定字段
 * @param {Object} obj 对象
 * @param {Array} keys 要排除的字段名数组
 * @returns {Object} 排除后的对象
 */
function omit(obj, keys) {
	if (!obj) return {};
	
	const result = { ...obj };
	keys.forEach(key => {
		delete result[key];
	});
	
	return result;
}

/**
 * 纯化数据，去除系统字段
 * 不改变原对象
 * @param {Object} data 待处理数据
 * @param {Array} ignoreFields 额外要忽略的字段
 * @returns {Object} 处理后的数据
 */
function pureData(data, ignoreFields = []) {
	if (!data) return null;
	
	const result = deepClone(data);
	
	// 系统字段
	const systemFields = [
		'_id', 
		'_openid', 
		'_createTime', 
		'_updateTime',
		'PID', 
		'USER_ID',
		'USER_OPEN_ID'
	].concat(ignoreFields);
	
	return omit(result, systemFields);
}

/**
 * 将对象数组按指定字段分组
 * @param {Array} array 对象数组
 * @param {string} key 分组字段
 * @returns {Object} 分组结果
 */
function groupBy(array, key) {
	if (!array || !array.length) return {};
	
	return array.reduce((result, item) => {
		const groupKey = item[key];
		if (!result[groupKey]) {
			result[groupKey] = [];
		}
		result[groupKey].push(item);
		return result;
	}, {});
}

/**
 * 将数组转为对象
 * @param {Array} array 数组
 * @param {string} key 作为对象键的属性名
 * @returns {Object} 转换后的对象
 */
function toObject(array, key) {
	if (!array || !array.length) return {};
	
	return array.reduce((result, item) => {
		if (item[key]) {
			result[item[key]] = item;
		}
		return result;
	}, {});
}

/**
 * 检查值是否已定义（非undefined和null）
 * @param {*} value 要检查的值
 * @returns {boolean} 是否已定义
 */
function isDefined(value) {
	return value !== undefined && value !== null;
}

/**
 * 检查字符串是否为有效的JSON
 * @param {string} str 字符串
 * @returns {boolean} 是否为有效的JSON
 */
function isValidJSON(str) {
	if (typeof str !== 'string') return false;
	
	try {
		JSON.parse(str);
		return true;
	} catch (e) {
		return false;
	}
}

/**
 * 安全地将字符串转换为JSON
 * @param {string} str 字符串
 * @param {*} defaultValue 转换失败时的默认值
 * @returns {Object} 转换结果
 */
function safeParseJSON(str, defaultValue = null) {
	try {
		return JSON.parse(str);
	} catch (e) {
		return defaultValue;
	}
}

/**
 * 格式化数字，如果为NaN则返回默认值
 * @param {string|number} num 数字或数字字符串
 * @param {number} defaultValue 默认值
 * @returns {number} 格式化后的数字
 */
function formatNumber(num, defaultValue = 0) {
	const result = Number(num);
	return isNaN(result) ? defaultValue : result;
}

/**
 * 判断是否为数组
 * @param {*} arr 要检查的值
 * @returns {boolean} 是否为数组
 */
function isArray(arr) {
	return Array.isArray(arr);
}

/**
 * 判断是否为对象
 * @param {*} obj 要检查的值
 * @returns {boolean} 是否为对象
 */
function isObject(obj) {
	return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
}

/**
 * 判断是否为空（undefined、null、空字符串、空数组、空对象）
 * @param {*} value 要检查的值
 * @returns {boolean} 是否为空
 */
function isEmpty(value) {
	if (value === undefined || value === null) return true;
	if (typeof value === 'string') return value.trim() === '';
	if (Array.isArray(value)) return value.length === 0;
	if (typeof value === 'object') return Object.keys(value).length === 0;
	return false;
}

/**
 * 生成一个特定范围内的随机数
 */
const genRandomNum = (min, max) => (Math.random() * (max - min + 1) | 0) + min;

/**
 * 生成一个随机的数字字母字符串 
 */
const genRandomString = len => {
	const text = 'abcdefghijklmnopqrstuvwxyz0123456789';
	const rdmIndex = text => Math.random() * text.length | 0;
	let rdmString = '';
	for (; rdmString.length < len; rdmString += text.charAt(rdmIndex(text)));
	return rdmString;
}

/**
 * 生成一个随机的字母字符串 
 */
const genRandomAlpha = len => {
	const text = 'abcdefghijklmnopqrstuvwxyz';
	const rdmIndex = text => Math.random() * text.length | 0;
	let rdmString = '';
	for (; rdmString.length < len; rdmString += text.charAt(rdmIndex(text)));
	return rdmString;
}

// 标签格式化
function fmtTag(tag) {
	if (!tag || tag.constructor != String) return '';
	tag = tag.trim();
	tag = tag.replace(/，/g, ',');

	let arr = tag.split(',');
	let newArr = [];
	for (let k = 0; k < arr.length; k++) {
		arr[k] = arr[k].trim();
		if (arr[k].length > 0) newArr.push(arr[k]);
	}
	return newArr.join(',');
}

// 拆分一维数组为二维数组
function spArr(arr, size) {
	if (!arr || !Array.isArray(arr) || arr.length == 0) return arr;

	let newArray = [];
	let index = 0;
	while (index < arr.length) {
		newArray.push(arr.slice(index, index += size));
	}
	return newArray;
}

/**
 * 把字符串格式化为数组
 * @param {*} str 
 * @param {*} sp 
 */
function str2Arr(str, sp = ',') {
	if (str && Array.isArray(str)) return str;

	str = str.replace(/，/g, sp);
	let arr = str.split(sp);
	for (let i = 0; i < arr.length; i++) {
		arr[i] = arr[i].trim();

		if (isNumber(arr[i])) {
			arr[i] = Number(arr[i]);
		}

	}
	return arr;
}

/**
 *  校验只要是数字（包含正负整数，0以及正负浮点数）就返回true 
 * @param {*} val 
 * @returns bool
 */
function isNumber(val) {
	var reg = /^[0-9]+.?[0-9]*$/;
	if (reg.test(val)) {
		return true;
	} else {
		return false;
	}
}

/**
 * 提取对象数组的某个属性数组,如[{'x':1},{'x':2}] 提取 x得到[1,2]
 * @param {*} arr 
 * @param {*} key 
 * @returns []
 */
function getArrByKey(arr, key) {
	if (!Array.isArray(arr)) return;
	return arr.map((item) => {
		return item[key]
	});
}

/**
 * 提取对象数组的多个属性数组, 
 * 如 [{'x':1,'y':11,'z':111},{'x':2,'y':22,'z':222}] 
 * 提取 ['x','y'] 得到[{'x':1,'y':11},{'x':2,'y':22}]
 * @param {*} arr 
 * @param {*} keys 
 * @returns []
 */
function getArrByKeyMulti(arr, keys = []) {
	if (!Array.isArray(arr)) return;
	if (!Array.isArray(keys)) return;

	let ret = [];
	for (let k = 0; k < arr.length; k++) {
		let node = {};
		for (let j in keys) {
			node[keys[j]] = arr[k][keys[j]];
		}
		ret.push(node);
	}

	return ret;
}

/**
 * 提取对象数组某个键值等于某值的对象数据
 * @param {*} arr 
 * @param {*} key  
 * @param {*} val 
 * @returns object {}
 */
function getDataByKey(arr, key, val) { 
	if (!Array.isArray(arr)) return null;

	for (let k = 0; k < arr.length; k++) {
		if (arr[k][key] == val)
			return arr[k];
	}

	return null;
}

/**
 * 文本内容格式化处理
 * @param {*} content 
 * @param {*} len 截取长度 -1不截取
 */
function fmtText(content, len = -1) {
	if (!content) return content;
	
	let str = content.replace(/[\r\n]/g, ""); //去掉回车换行
	if (len > 0) {
		str = str.substr(0, len);
	}
	return str.trim();
}

// 下划线转换驼峰
function toHump(name) {
	name = name.replace(/\_(\w)/g, function (all, letter) {
		return letter.toUpperCase();
	});

	// 首字母大写 
	let firstChar = name.charAt(0).toUpperCase();
	return firstChar + name.slice(1);
}

// 驼峰转换下划线
function toLine(name) {
	name = name.replace(/([A-Z])/g, "_$1").toLowerCase();

	//如果首字符为下划线，干掉
	if (name.charAt(0) === '_')
		return name.slice(1);
	else
		return name;
}

// 金额格式化 dot为金额每隔三位用","或" "间隔
function fmtMoneyShow(s, dot = ',', prefix = '¥') {
	if (s === '' || s === null || s === undefined) s = 0;
	
	s = parseFloat((s + "").replace(/[^\d\.-]/g, "")).toFixed(2) + "";
	var l = s.split(".")[0].split("").reverse(),
		r = s.split(".")[1];
	t = "";
	for (i = 0; i < l.length; i++) {
		t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? dot : "");
	}
	return prefix + t.split("").reverse().join("") + "." + r;
}

// 金额格式化 不能超过16位
function fmtMoney(money) {
	money = Number(money);
	if (!money) money = 0;
	money = money.toFixed(2)
	money = Number(money);
	return money;
}


/**
 *简单数组转对象数组
 * @param {*} arr [1,2,3]
 * @param {*} key [x1,x2,x3]
 * @returns [{x1:1,x2:1,x3:1},{x1:2,x2:2,x3:2},{x1:3,x2:3,x3:3}]
 */
function arr2ObjectArr(arr, key1, key2, key3) {
	let ret = [];
	for (let k = 0; k < arr.length; k++) {
		let obj = {};
		if (key1) obj[key1] = arr[k];
		if (key2) obj[key2] = arr[k];
		if (key3) obj[key3] = arr[k];
		ret.push(obj);
	}
	return ret;
}

/**
 * property
 * @param {*} property 排序属性
 * @returns 排序好的数组 
 * 用法 arr.sort(compare('age'))
 */
function objArrSortAsc(property) {
	return function (a, b) {
		var value1 = a[property];
		var value2 = b[property];
		if (value1 < value2)
			return -1;
		else if (value1 > value2)
			return 1;
		else return 0;
	}
}

/**
 * property
 * @param {*} property 排序属性
 * @returns 排序好的数组 
 * 用法 arr.sort(compare('age'))
 */
function objArrSortDesc(property) {
	return function (a, b) {
		var value1 = a[property];
		var value2 = b[property];
		if (value1 < value2)
			return 1;
		else if (value1 > value2)
			return -1;
		else return 0;
	}
}

/**
 * 数组有则减少，无则增加
 * @param {*} arr 
 * @param {*} data 
 * @param {*} sort 排序方式 asc/desc
 */
function arrAddDel(arr, data, sort = 'asc') {
	if (!arr) return arr;
	if (!Array.isArray(arr)) return arr;

	let idx = arr.indexOf(data);
	if (idx > -1)
		arr.splice(idx, 1);
	else
		arr.push(data)

	if (sort == 'asc')
		return arr.sort();
	else
		return arr.reverse();
} 

function padLeft(str, len, charStr) {
	if (!str)
		str = '';
	else
		str = str + '';
	return new Array(len - str.length + 1).join(charStr || '') + str;
}

function padRight(str, len, charStr) {
	if (!str)
		str = '';
	else
		str = str + '';
	return str + new Array(len - str.length + 1).join(charStr || '');
}


// 选项表单处理
function getSelectOptions(str) {
	if (!str)
		return [];
	else if (str.includes('=')) {
		let arr = str.split(',');
		for (let k = 0; k < arr.length; k++) {
			let node = arr[k].split('=');
			arr[k] = {};

			let labelArr = node[1].split('|');
			arr[k].label = labelArr[0];
			if (labelArr.length > 1) { //扩展属性
				arr[k].ext = labelArr[1];
			}
			arr[k].val = node[0];
		}
		return arr;
	} else {
		return str.split(',');
	}
}

// 数组元素交换位置 index1和index2分别是两个数组的索引值
function arraySwap(arr, index1, index2) {
	arr[index1] = arr.splice(index2, 1, arr[index1])[0];
	return arr;
}

// 数组置顶
function arrayTop(arr, idx) {
	let node = arr.splice(idx, 1)[0];
	arr.unshift(node);
	return arr;
}

// 数组置底
function arrayBottom(arr, idx) {
	let node = arr.splice(idx, 1)[0];
	arr.push(node);
	return arr;
}

/**
 * 把某个值/对象按key插到某个对象数组
 * @param {*} arr  目标数组
 * @param {*} key  键
 * @param {*} val  判断值
 * @param {*} obj  插入对象{}
 */
function insertObjArrByKey(arr, key, val, obj) {
	if (!arr) return arr;

	for (let k = 0; k < arr.length; k++) {
		if (arr[k][key] == val) {
			// 发现存在
			arr[k].list.push(obj);
			return arr;
		}
	}

	// 不存在
	let newObj = {
		[key]: val,
		list: [obj]
	}
	arr.push(newObj);
	return arr;
}

/**
 * 从对象数组中， 根据某个键值 获取满足的对象
 * @param {*} arr 
 * @param {*} key 
 * @param {*} val 
 */
function getValFromArr(arr, key = 'val', val = '') {
	if (!Array.isArray(arr)) return null;
	for (let k = 0; k < arr.length; k++) {
		if (arr[k][key] == val)
			return arr[k];
	}

	return null;
}

// 导出所有函数
module.exports = {
	arrayTop,
	arraySwap,
	arrayBottom,

	fmtTag,

	getValFromArr,
	getArrByKey,
	getArrByKeyMulti, //提取对象数组的多个属性数组
	spArr, //拆分一维数组为二维
	getDataByKey,
	str2Arr,
	arr2ObjectArr,
	insertObjArrByKey,
	arrAddDel,
	objArrSortAsc,
	objArrSortDesc,

	arrAddDel,
	isNumber,

	padLeft,
	padRight,

	genRandomString, // 随机字符串
	genRandomAlpha,
	genRandomNum, // 随机数字 
	fmtText, // 文本内容格式化处理
	fmtMoneyShow, //金额格式化
	fmtMoney,

	toHump,
	toLine,

	getSelectOptions, //选项表单处理

	deepClone,
	pick,
	omit,
	pureData,
	groupBy,
	toObject,
	isDefined,
	isValidJSON,
	safeParseJSON,
	formatNumber,
	isArray,
	isObject,
	isEmpty
}